home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / non-ANSI / ipopd / ipop3d.c < prev    next >
C/C++ Source or Header  |  1997-02-25  |  16KB  |  599 lines

  1. /*
  2.  * Program:    IPOP3D - IMAP2 to POP3 conversion server
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    1 November 1990
  13.  * Last Edited:    25 February 1997
  14.  *
  15.  * Copyright 1997 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. /* Parameter files */
  38.  
  39. #include "mail.h"
  40. #include "osdep.h"
  41. #include <stdio.h>
  42. #include <ctype.h>
  43. #include <netdb.h>
  44. #include <errno.h>
  45. extern int errno;        /* just in case */
  46. #include <signal.h>
  47. #include <pwd.h>
  48. #include "misc.h"
  49.  
  50.  
  51. /* Autologout timer */
  52. #define TIMEOUT 60*30
  53.  
  54.  
  55. /* Login tries */
  56. #define LOGTRY 3
  57.  
  58.  
  59. /* Size of temporary buffers */
  60. #define TMPLEN 1024
  61.  
  62.  
  63. /* Server states */
  64.  
  65. #define AUTHORIZATION 0
  66. #define TRANSACTION 1
  67. #define UPDATE 2
  68.  
  69. /* Eudora food */
  70.  
  71. #define STATUS "Status: %s%s\015\012\015\012"
  72. #define SLEN (sizeof (STATUS)-5)
  73.  
  74.  
  75. /* Global storage */
  76.  
  77. char *version = "3.3(20)";    /* server version */
  78. int state = AUTHORIZATION;    /* server state */
  79. MAILSTREAM *stream = NIL;    /* mailbox stream */
  80. int logtry = LOGTRY;        /* login tries */
  81. long nmsgs = 0;            /* current number of messages */
  82. long last = 0;            /* highest message accessed */
  83. long il = 0;            /* initial last message */
  84. char *host = NIL;        /* remote host name */
  85. char *user = NIL;        /* user name */
  86. char *pass = NIL;        /* password */
  87. long *msg = NIL;        /* message translation vector */
  88.  
  89.  
  90. /* Function prototypes */
  91.  
  92. void main  ();
  93. void clkint  ();
  94. void kodint  ();
  95. int login  ();
  96. long blat  ();
  97.  
  98. /* Main program */
  99.  
  100. void main (argc,argv)
  101.     int argc;
  102.     char *argv[];
  103. {
  104.   long i,j,k;
  105.   char *s,*t;
  106.   struct hostent *hst;
  107.   char tmp[TMPLEN];
  108. #include "linkage.c"
  109.   openlog ("ipop3d",LOG_PID,LOG_MAIL);
  110.   gethostname (tmp,TMPLEN-1);/* get local name */
  111.   s = cpystr ((hst = gethostbyname (tmp)) ? hst->h_name : tmp);
  112.   printf ("+OK %s POP3 %s w/IMAP2 client %s",s,version,
  113.       "(Comments to MRC@CAC.Washington.EDU)");
  114.   rfc822_date (tmp);        /* get date/time now */
  115.   printf (" at %s\015\012",tmp);
  116.   fflush (stdout);        /* dump output buffer */
  117.   signal (SIGALRM,clkint);    /* prepare for clock interrupt */
  118.   signal (SIGUSR2,kodint);    /* prepare for Kiss Of Death */
  119.   while (state != UPDATE) {    /* command processing loop */
  120.     alarm (TIMEOUT);        /* get a command under timeout */
  121.     while (!fgets (tmp,TMPLEN-1,stdin)) {
  122.       if (errno==EINTR) errno=0;/* ignore if some interrupt */
  123.       else {
  124.     syslog (LOG_INFO,"Connection broken while reading line from %.80s",
  125.         tcp_clienthost (tmp));
  126.     _exit (1);
  127.       }
  128.     }
  129.     alarm (0);            /* make sure timeout disabled */
  130.                 /* find end of line */
  131.     if (!strchr (tmp,'\012')) puts ("-ERR Command line too long\015");
  132.     else if (!(s = strtok (tmp," \015\012"))) puts ("-ERR Null command\015");
  133.     else {            /* dispatch based on command */
  134.       ucase (s);        /* canonicalize case */
  135.                 /* snarf argument */
  136.       t = strtok (NIL,"\015\012");
  137.                 /* QUIT command always valid */
  138.       if (!strcmp (s,"QUIT")) state = UPDATE;
  139.       else switch (state) {    /* else dispatch based on state */
  140.       case AUTHORIZATION:    /* waiting to get logged in */
  141.     if (!strcmp (s,"USER")) {
  142.       fs_give ((void **) &host);
  143.       fs_give ((void **) &user);
  144.       if (t && *t) {    /* if user name given */
  145.                 /* remote user name? */
  146.         if (s = strchr (t,':')) {
  147.           *s++ = '\0';    /* tie off host name */
  148.           host = cpystr (t);/* copy host name */
  149.           user = cpystr (s);/* copy user name */
  150.         }
  151.                 /* local user name */
  152.         else user = cpystr (t);
  153.         puts ("+OK User name accepted, password please\015");
  154.       }
  155.       else puts ("-ERR Missing username argument\015");
  156.     }
  157.     else if (user && *user && !strcmp (s,"PASS"))
  158.       state = login (t,argc,argv);
  159.                 /* (chuckle) */
  160.     else if (!strcmp (s,"RPOP")) puts ("-ERR Nice try, bunkie\015");
  161.     else puts ("-ERR Unknown command in AUTHORIZATION state\015");
  162.     break;
  163.  
  164.       case TRANSACTION:        /* logged in */
  165.     if (!strcmp (s,"STAT")) {
  166.       for (i = 1,j = 0,k = 0; i <= nmsgs; i++)
  167.         if (msg[i] > 0) {    /* message still exists? */
  168.           j++;        /* count one more undeleted message */
  169.           k += mail_elt (stream,msg[i])->rfc822_size + SLEN;
  170.         }
  171.       printf ("+OK %d %d\015\012",j,k);
  172.     }
  173.     else if (!strcmp (s,"LIST")) {
  174.       if (t && *t) {    /* argument do single message */
  175.         if (((i = atoi (t)) > 0) && (i <= nmsgs) && (msg[i] >0))
  176.           printf ("+OK %d %d\015\012",i,
  177.               mail_elt(stream,msg[i])->rfc822_size + SLEN);
  178.         else puts ("-ERR No such message\015");
  179.       }
  180.       else {        /* entire mailbox */
  181.         puts ("+OK Mailbox scan listing follows\015");
  182.         for (i = 1,j = 0,k = 0; i <= nmsgs; i++) if (msg[i] > 0)
  183.           printf ("%d %d\015\012",i,
  184.               mail_elt (stream,msg[i])->rfc822_size + SLEN);
  185.         puts (".\015");    /* end of list */
  186.       }
  187.     }
  188.     else if (!strcmp (s,"RETR")) {
  189.       if (t && *t) {    /* must have an argument */
  190.         if (((i = atoi (t)) > 0) && (i <= nmsgs) && (msg[i] > 0)) {
  191.           MESSAGECACHE *elt;
  192.                 /* update highest message accessed */
  193.           if (i > last) last = i;
  194.           printf ("+OK %d octets\015\012",
  195.               (elt = mail_elt (stream,msg[i]))->rfc822_size + SLEN);
  196.                 /* output message */
  197.           j = strlen (t = cpystr (mail_fetchheader (stream,msg[i])));
  198.           t[j - 2] = '\0';    /* flush trailing CRLF */
  199.           blat (t,-1);    /* output header */
  200.           fs_give ((void **) &t);
  201.           printf (STATUS,elt->seen ? "R" : " ",elt->recent ? " " : "O");
  202.           blat (mail_fetchtext (stream,msg[i]),-1);
  203.           puts (".\015");    /* end of list */
  204.         }
  205.         else puts ("-ERR No such message\015");
  206.       }
  207.       else puts ("-ERR Missing message number argument\015");
  208.     }
  209.     else if (!strcmp (s,"DELE")) {
  210.       if (t && *t) {    /* must have an argument */
  211.         if (((i = atoi (t)) > 0) && (i <= nmsgs) && (msg[i] > 0)) {
  212.                 /* update highest message accessed */
  213.           if (i > last) last = i;
  214.                 /* delete message */
  215.           sprintf (tmp,"%d",msg[i]);
  216.           mail_setflag (stream,tmp,"\\Deleted");
  217.           msg[i] = -msg[i];    /* note that we deleted this message */
  218.           puts ("+OK Message deleted\015");
  219.         }
  220.         else puts ("-ERR No such message\015");
  221.       }
  222.       else puts ("-ERR Missing message number argument\015");
  223.     }
  224.  
  225.     else if (!strcmp (s,"NOOP")) puts ("+OK No-op to you too!\015");
  226.     else if (!strcmp (s,"LAST")) printf ("+OK %d\015\012",last);
  227.     else if (!strcmp (s,"RSET")) {
  228.       if (nmsgs) {        /* undelete and unmark all of our messages */
  229.         for (i = 1; i <= nmsgs; i++) {
  230.                 /* ugly and inefficient, but trustworthy */
  231.           if (msg[i] < 0) {
  232.         sprintf (tmp,"%d",msg[i] = -msg[i]);
  233.         mail_clearflag (stream,tmp,i <= il ? "\\Deleted" :
  234.                   "\\Deleted \\Seen");
  235.           }
  236.           else if (i > il) {
  237.         sprintf (tmp,"%d",msg[i]);
  238.         mail_clearflag (stream,tmp,"\\Seen");
  239.           }
  240.         }
  241.         last = il;
  242.       }
  243.       puts ("+OK Reset state\015");
  244.     }
  245.     else if (!strcmp (s,"TOP")) {
  246.       if (t && *t) {    /* must have an argument */
  247.         if (((i = strtol (t,&t,10)) > 0) && (i <= nmsgs) && t && *t &&
  248.         ((j = atoi (t)) >= 0) && (msg[i] > 0)) {
  249.           MESSAGECACHE *elt = mail_elt (stream,msg[i]);
  250.                 /* update highest message accessed */
  251.           if (i > last) last = i;
  252.           puts ("+OK Top of message follows\015");
  253.                 /* output message */
  254.           k = strlen (t = cpystr (mail_fetchheader (stream,msg[i])));
  255.           t[k - 2] = '\0';    /* flush trailing CRLF */
  256.           j -= (blat (t,-1) + 2);
  257.           fs_give ((void **) &t);
  258.           printf (STATUS,elt->seen ? "R" : " ",elt->recent ? " " : "O");
  259.           if (j > 0) blat (mail_fetchtext (stream,msg[i]),j);
  260.           puts (".\015");    /* end of list */
  261.         }
  262.         else puts ("-ERR Bad argument or no such message\015");
  263.       }
  264.       else puts ("-ERR Missing message number argument\015");
  265.     }
  266.     else if (!strcmp (s,"XTND")) puts ("-ERR Sorry I can't do that\015");
  267.     else puts ("-ERR Unknown command in TRANSACTION state\015");
  268.     break;
  269.       default:
  270.         puts ("-ERR Server in unknown state\015");
  271.     break;
  272.       }
  273.     }
  274.     fflush (stdout);        /* make sure output finished */
  275.   }
  276.                 /* expunge mailbox if a stream open */
  277.   if (stream && nmsgs) mail_expunge (stream);
  278.   mail_close (stream);        /* clean up the stream */
  279.                 /* "now it's time to say sayonara..." */
  280.   if (logtry) puts ("+OK Sayonara\015");
  281.   fflush (stdout);        /* make sure output finished */
  282.   syslog (LOG_INFO,"Logout from %.80s",tcp_clienthost (tmp));
  283.   exit (0);            /* all done */
  284. }
  285.  
  286. /* Clock interrupt
  287.  */
  288.  
  289. void clkint ()
  290. {
  291.   char tmp[MAILTMPLEN];
  292.   puts ("-ERR Autologout; idle for too long\015");
  293.   syslog (LOG_INFO,"Autologout user=%.80s host=%.80s",user ? user : "???",
  294.       tcp_clienthost (tmp));
  295.   fflush (stdout);        /* make sure output blatted */
  296.   mail_close (stream);        /* try to gracefully close the stream */
  297.   stream = NIL;
  298.   exit (0);            /* die die die */
  299. }
  300.  
  301.  
  302. /* Kiss Of Death interrupt
  303.  */
  304.  
  305. void kodint ()
  306. {
  307.   char tmp[MAILTMPLEN];
  308.   puts ("-ERR Received Kiss of Death\015");
  309.   syslog (LOG_INFO,"Kiss of death user=%.80s host=%.80s",user ? user : "???",
  310.       tcp_clienthost (tmp));
  311.   fflush (stdout);        /* make sure output blatted */
  312.   mail_close (stream);        /* try to gracefully close the stream */
  313.   stream = NIL;
  314.   exit (0);            /* die die die */
  315. }
  316.  
  317. /* Parse PASS command
  318.  * Accepts: pointer to command argument
  319.  * Returns: new state
  320.  */
  321.  
  322. int login (t,argc,argv)
  323.     char *t;
  324.     int argc;
  325.     char *argv[];
  326. {
  327.   long i,j;
  328.   char tmp[TMPLEN];
  329.   struct passwd *pwd = getpwnam ("nobody");
  330.   MESSAGECACHE *elt;
  331.   fs_give ((void **) &pass);    /* flush old passowrd */
  332.   if (!(t && *t)) {        /* if no password given */
  333.     puts ("-ERR Missing password argument\015");
  334.     return AUTHORIZATION;
  335.   }
  336.   pass = cpystr (t);        /* copy password argument */
  337.   if (host) {            /* remote; build remote INBOX */
  338.     syslog (LOG_INFO,"IMAP login to host=%.80s user=%.80s host=%.80s",host,
  339.         user,tcp_clienthost (tmp));
  340.     sprintf (tmp,"{%s}INBOX",host);
  341.     if (pwd) {            /* try to become someone harmless */
  342.       setgid (pwd->pw_gid);    /* set group ID */
  343.       setuid (pwd->pw_uid);    /* and user ID */
  344.     }
  345.   }
  346.                 /* local; attempt login, select INBOX */
  347.   else if (((i = strlen (user)) < 64) &&
  348.        server_login (user,pass,NIL,argc,argv)) {
  349.     syslog (LOG_INFO,"Login user=%.80s host=%.80s",user,tcp_clienthost (tmp));
  350.     strcpy (tmp,"INBOX");
  351.   }
  352.   else {
  353.     if (i > 128) syslog (LOG_ALERT|LOG_AUTH,"Crack attempt, host=%.80s",
  354.              tcp_clienthost (tmp));
  355.     sleep (3);            /* slow the cracker down */
  356.     if (--logtry) {        /* vague error message to confuse crackers */
  357.       puts ("-ERR Bad login\015");
  358.       syslog (LOG_INFO,"Login failure user=%.80s host=%.80s",user,
  359.           tcp_clienthost (tmp));
  360.       return AUTHORIZATION;
  361.     }
  362.     fputs ("-ERR Too many login failures\015\012",stdout);
  363.     syslog (LOG_INFO,"Excessive login failures user=%.80s host=%.80s",user,
  364.         tcp_clienthost (tmp));
  365.     return UPDATE;
  366.   }
  367.   nmsgs = 0;            /* no messages yet */
  368.   if (msg) fs_give ((void **) &msg);
  369.                 /* if mailbox non-empty */
  370.   if ((stream = mail_open (stream,tmp,NIL)) && (j = stream->nmsgs)) {
  371.     sprintf (tmp,"1:%d",j);    /* fetch fast information for all messages */
  372.     mail_fetchfast (stream,tmp);
  373.     msg = (long *) fs_get ((stream->nmsgs + 1) * sizeof (long));
  374.     for (i = 1; i <= j; i++) if (!(elt = mail_elt (stream,i))->deleted) {
  375.       msg[++nmsgs] = i;        /* note the presence of this message */
  376.       if (elt->seen) il = last = nmsgs;
  377.     }
  378.   }
  379.   printf ("+OK Mailbox open, %d messages\015\012",nmsgs);
  380.   return TRANSACTION;
  381. }
  382.  
  383. /* Blat a string with dot checking
  384.  * Accepts: string
  385.  *        maximum number of lines if greater than zero
  386.  * Returns: number of lines output
  387.  * This routine is uglier and kludgier than it should be, just to be robust
  388.  * in the case of a Tenex-format message which doesn't end in a newline.
  389.  */
  390.  
  391. long blat (text,lines)
  392.     char *text;
  393.     long lines;
  394. {
  395.   char c = *text++;
  396.   char d = *text++;
  397.   char e;
  398.   long ret = 0;
  399.                 /* no-op if zero lines or empty string */
  400.   if (!(lines && c && d)) return;
  401.   if (c == '.') putchar ('.');    /* double string-leading dot if necessary */
  402.   while (e = *text++) {        /* copy loop */
  403.     putchar (c);        /* output character */
  404.     if (c == '\012') {        /* end of line? */
  405.       ret++;            /* count another line */
  406.       if (!--lines) return ret;    /* count down another line, return if done */
  407.                 /* double leading dot as necessary */
  408.       if (d == '.') putchar ('.');
  409.     }
  410.     c = d; d = e;        /* move to next character */
  411.   }
  412.   puts ("\015");        /* output newline instead of last 2 chars */
  413.   return ++ret;
  414. }
  415.  
  416. /* Co-routines from MAIL library */
  417.  
  418.  
  419. /* Message matches a search
  420.  * Accepts: MAIL stream
  421.  *        message number
  422.  */
  423.  
  424. void mm_searched (stream,msgno)
  425.     MAILSTREAM *stream;
  426.     long msgno;
  427. {
  428.   /* Never called */
  429. }
  430.  
  431.  
  432. /* Message exists (mailbox)
  433.     i.e. there are that many messages in the mailbox;
  434.  * Accepts: MAIL stream
  435.  *        message number
  436.  */
  437.  
  438. void mm_exists (stream,number)
  439.     MAILSTREAM *stream;
  440.     long number;
  441. {
  442.   /* Can't use this mechanism.  POP has no means of notifying the client of
  443.      new mail during the session. */
  444. }
  445.  
  446.  
  447. /* Message expunged
  448.  * Accepts: MAIL stream
  449.  *        message number
  450.  */
  451.  
  452. void mm_expunged (stream,number)
  453.     MAILSTREAM *stream;
  454.     long number;
  455. {
  456.   /* This isn't used */
  457. }
  458.  
  459.  
  460. /* Message flag status change
  461.  * Accepts: MAIL stream
  462.  *        message number
  463.  */
  464.  
  465. void mm_flags (stream,number)
  466.     MAILSTREAM *stream;
  467.     long number;
  468. {
  469.   /* This isn't used */
  470. }
  471.  
  472.  
  473. /* Mailbox found
  474.  * Accepts: Mailbox name
  475.  */
  476.  
  477. void mm_mailbox (string)
  478.     char *string;
  479. {
  480.   /* This isn't used */
  481. }
  482.  
  483.  
  484. /* BBoard found
  485.  * Accepts: BBoard name
  486.  */
  487.  
  488. void mm_bboard (string)
  489.     char *string;
  490. {
  491.   /* This isn't used */
  492. }
  493.  
  494. /* Notification event
  495.  * Accepts: MAIL stream
  496.  *        string to log
  497.  *        error flag
  498.  */
  499.  
  500. void mm_notify (stream,string,errflg)
  501.     MAILSTREAM *stream;
  502.     char *string;
  503.     long errflg;
  504. {
  505.   mm_log (string,errflg);    /* just do mm_log action */
  506. }
  507.  
  508.  
  509. /* Log an event for the user to see
  510.  * Accepts: string to log
  511.  *        error flag
  512.  */
  513.  
  514. void mm_log (string,errflg)
  515.     char *string;
  516.     long errflg;
  517. {
  518.   /* Not doing anything here for now */
  519. }
  520.  
  521.  
  522. /* Log an event to debugging telemetry
  523.  * Accepts: string to log
  524.  */
  525.  
  526. void mm_dlog (string)
  527.     char *string;
  528. {
  529.   /* Not doing anything here for now */
  530. }
  531.  
  532.  
  533. /* Get user name and password for this host
  534.  * Accepts: host name
  535.  *        where to return user name
  536.  *        where to return password
  537.  *        trial count
  538.  */
  539.  
  540. void mm_login (host,username,password,trial)
  541.     char *host;
  542.     char *username;
  543.     char *password;
  544.     long trial;
  545. {
  546.   strcpy (username,user);    /* set user name */
  547.   strcpy (password,pass);    /* and password */
  548. }
  549.  
  550. /* About to enter critical code
  551.  * Accepts: stream
  552.  */
  553.  
  554. void mm_critical (stream)
  555.     MAILSTREAM *stream;
  556. {
  557.   /* Not doing anything here for now */
  558. }
  559.  
  560.  
  561. /* About to exit critical code
  562.  * Accepts: stream
  563.  */
  564.  
  565. void mm_nocritical (stream)
  566.     MAILSTREAM *stream;
  567. {
  568.   /* Not doing anything here for now */
  569. }
  570.  
  571.  
  572. /* Disk error found
  573.  * Accepts: stream
  574.  *        system error code
  575.  *        flag indicating that mailbox may be clobbered
  576.  * Returns: abort flag
  577.  */
  578.  
  579. long mm_diskerror (stream,errcode,serious)
  580.     MAILSTREAM *stream;
  581.     long errcode;
  582.     long serious;
  583. {
  584.   syslog (LOG_ALERT,"Retrying after disk error %.80s",strerror (errcode));
  585.   sleep (5);            /* can't do much better than this! */
  586.   return NIL;
  587. }
  588.  
  589.  
  590. /* Log a fatal error event
  591.  * Accepts: string to log
  592.  */
  593.  
  594. void mm_fatal (string)
  595.     char *string;
  596. {
  597.   mm_log (string,ERROR);    /* shouldn't happen normally */
  598. }
  599.